home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / PCI Driver Sample / NCR_DriverProject / Src / NCRScriptManager.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-05  |  22.2 KB  |  640 lines  |  [TEXT/MPCC]

  1. /*                                    NCRScriptManager.c                                */
  2. /*
  3.  * NCRScriptUtilities.c
  4.  * Copyright © 1994-95 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | This file is specific to the NCR chip: it contains the scripts that the chip will    |
  8.     | execute and some utilities to patch the script labels. In a production system,    |
  9.     | the script will be pre-compiled and stores in read-only memory. Well, that's the    |
  10.     | theory: in reality, NCR 53C825 scripts will probably need to be patched when the    |
  11.     | system is initialized in order to store scratch area physical addresses. We don't    |
  12.     | do this, however.                                                                    |
  13.     .___________________________________________________________________________________.
  14. */
  15.  
  16. #include "NCRDriverPrivate.h"
  17. #include <stddef.h>
  18.  
  19. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  20.  *
  21.  * The script is generated by macros. As shown here, it is in Macintosh Big-endian
  22.  * order. The NCR chip reads it one longword at a time using DMA. Because the PCI bus
  23.  * is little-endian, we must reverse bytes. This will be done the first time that
  24.  * the driver is called. In a production driver, the script would be generated by an
  25.  * external tool and stored in the driver in its final little-endian form.
  26.  *
  27.  * The script "compiler" uses the following conventions:
  28.  *        offsetXXX            An offset into the per-request data area. The offset will
  29.  *                            reference a Table entry for a Move or Select operation.
  30.  *        'XXXX'                A label within the script itself. This is used for jump
  31.  *                            resolutions. To simplify the "compiler," these are
  32.  *                            post-processed when the first time the script is executed:
  33.  *                            Labels are four-byte character constants. The code that
  34.  *                            resolves labels is moderately crude.
  35.  * The macros:
  36.  *        Label(name)
  37.  *                            Define a label. This actually creates a NOP script
  38.  *                            operation with the label in the second longword.
  39.  *        Jump(label)
  40.  *        JumpWhenPhase(phase, label)
  41.  *        JumpIfPhase(phase, label)
  42.  *        JumpIfValue(value, label)
  43.  *        JumpIfNotCarry(label)
  44.  *                            Jump (always, or if the condition is true) to the label.
  45.  *        ShiftRight(register)
  46.  *                            This executes MOVE register SHR to register
  47.  *        MoveWhen(offset, count, phase)
  48.  *        MoveFromWhen(offset, phase)
  49.  *                            Move data to/from the SCSI bus using the specified data
  50.  *                            offset and count, or table. The ...When variant interrupts
  51.  *                            if a phase mismatch occurs (this would be a serious error).
  52.  *        MoveValueToRegister(constant, register)
  53.  *                            Store a constant value into a register.
  54.  *        MoveRegister(srcReg, dstReg)
  55.  *                            Move values between registers. Note that one of the
  56.  *                            registers must be SFBR to move between any two registers.
  57.  *                                MoveRegister(foo, SFBR)
  58.  *                                MoveRegister(SFBR, bar)
  59.  *        Int(value)
  60.  *                            Interrupt the host processor.
  61.  *        AllowDisconnect(label)
  62.  *                            Clear the "interrupt on bus free" bit:
  63.  *                                Move SCNTL2 & 0x7F to SCNTL2
  64.  *        WaitDisconnect(label)
  65.  *                            Wait until the bus is free
  66.  *        SelectATN(offset, label)
  67.  *                            Select with ATN using the table offset; jump to the label
  68.  *                            if we are selected or reselected.
  69.  *        ClearACK(label)
  70.  *                            Clear ACK to accept message bytes.
  71.  *        ClearATN(label)
  72.  *                            Clear the ATN line.
  73.  */
  74. #define NOP                    0x80000000            /* Jump never                        */
  75. #define OP_MASK                (bit31 | bit30)        /* Select the operator type            */
  76. #define IO_OP                (bit30)
  77. #define JUMP_OP                (bit31)
  78. #define MEMORY_MOVE_OP        (bit31 | bit30)
  79. #define OPCODE(op)            ((op) & OP_MASK)
  80. #define IsMemoryMoveOp(op)    (OPCODE(op) == MEMORY_MOVE_OP)
  81. #define IsLabel(op)            ((op) == NOP)
  82.  
  83. /*
  84.  * A label is a no-op instruction - the label name is in the second word. If you add
  85.  * instructions that take labels, be sure to update the kMaxLabels parameter to ensure
  86.  * that the label resolver has enough space.
  87.  *
  88.  * Scripts always start with the ScriptStart instruction - this is used to identify
  89.  * compiled scripts.
  90.  *
  91.  */
  92. #define Label(label)                    NOP, label
  93. #define ScriptStart                        Label(kScriptStartLabel)
  94.  
  95. /*
  96.  * All Jump instructions take a mandatory label.
  97.  */
  98. #define Jump(label)                        0x80880000,                            label
  99. #define JumpIfWhen(phase, mod, label) \
  100.                             (0x80800000 | ((phase) << 24) | (mod)),            label
  101. #define ifTrue    bit19
  102. #define ifPhase    bit17
  103. #define    ifWait    bit16
  104. #define JumpIfPhase(phase, label) \
  105.                             JumpIfWhen(phase, ifTrue | ifPhase, label)
  106. #define JumpWhenPhase(phase, label)    \
  107.                             JumpIfWhen(phase, ifWait | ifTrue | ifPhase, label)
  108. #define JumpWhenNotPhase(phase, label) \
  109.                             JumpIfWhen(phase, ifWait | ifPhase, label)
  110. #define JumpIfNotPhase(phase, label) \
  111.                             JumpIfWhen(phase, ifPhase, label)
  112. /*
  113.  * The NCR Mask causes bits to be ignored. We use a positive mask. I.e.,
  114.  * JumpIfMaskedEquals == (SFBR & mask) == value
  115.  */
  116. #define JumpIfMaskedEquals(mask, value, label)                        \
  117.                                         (0x808C0000                    \
  118.                                         | (((~mask) & 0xFF) << 8)    \
  119.                                         | value), label
  120. #define JumpIfEqual(value, label)         (0x808C0000 | value),                 label
  121. #define JumpIfMaskedNotEquals(mask, value, label) \
  122.                                         (0x80840000                    \
  123.                                         | (((~mask) & 0xFF) << 8)    \
  124.                                         | value), label
  125. #define JumpIfNotEqual(value, label)     (0x80840000 | value),                 label
  126.  
  127. #define JumpIfBitSet(value, label)        (0x808C0000 | (value << 8) | value), label
  128. #define JumpIfBitClear(value, label)    (0x80840000 | (value << 8) | value), label
  129. #define JumpIfNotCarry(label)            0x80A00000,                            label
  130. #define WaitDisconnect()                0x48000000,                            0
  131. #define SelectATN(offset, label)        (0x47000000 | offset),                label
  132. #define ClearACK()                        (0x60000000 | bit6),                0
  133. #define ClearATN()                        (0x60000000 | bit3),                0
  134. #define ClearTarget()                    (0x60000000 | bit9),                0
  135. /*
  136.  * MoveFrom copies data to/from the SCSI bus and memory. It is passed the offset
  137.  * to a data table that has the actual physical address and byte count. This
  138.  * could have been simpler, not that we're whining or anything, right?
  139.  */
  140. #define MoveFrom(offset, phase)            0x18000000 | (phase << 24),            offset
  141. /*
  142.  * Move/compute data using the NCR registers. Note that the #define'd registers
  143.  * are offset within our I/O space -- we remove this by and'ing with 0x7F
  144.  */
  145. #define MoveValueToRegister(val, reg) \
  146.     0x78000000 | ((val)<<8)    | (((reg) & 0x7F) << 16), 0
  147. #define OrValueToRegister(val, reg) \
  148.     0x7A000000 | ((val)<<8)    | (((reg) & 0x7F) << 16), 0
  149. #define AndValueToRegister(val, reg) \
  150.     0x7C000000 | ((val)<<8)    | (((reg) & 0x7F) << 16), 0
  151. /*
  152.  * This is actually a "register | zero -> SFBR"
  153.  */
  154. #define    MoveToSFBR(reg)    \
  155.     0x72000000                | (((reg) & 0x7F) << 16), 0
  156. /*
  157.  * This is actually a "SFBR | zero -> register"
  158.  */
  159. #define MoveFromSFBR(reg) \
  160.     0x6A000000                | (((reg) & 0x7F) << 16), 0
  161. #if 0
  162. /*
  163.  * This rotates the carry into the register high bit. We do not want this to happen.
  164.  */
  165. #define ShiftRight(reg)    \
  166.     0x7D000000                | (((reg) & 0x7F) << 16), 0
  167. #endif
  168. /*
  169.  * Interrupt the host processor.
  170.  */
  171. #define Int(status)                     0x98080000,    status
  172. /*
  173.  * Debug interrupts used to log why the script jumps to the failure script.
  174.  */
  175. #if USE_LOG_LIBRARY
  176. #define DebugInt(status)                Int(status),    /* Note trailing comma        */
  177. #else
  178. #define DebugInt(status)                /* Nothing */
  179. #endif
  180.  
  181. /*
  182.  * AllowDisconnect == move SCNTL2 & 0x7F to SCNTL2 -- prevents interrupt on disconnect
  183.  */
  184. #define AllowDisconnect()                0x7C027F00,    0x00000000
  185.  
  186. #define offsetDeviceID                offsetof(ScriptData, deviceIDTable)    /* zero        */
  187. #define offsetIDMsg                    offsetof(ScriptData, idMsgTable)
  188. #define offsetCommand                offsetof(ScriptData, commandTable)
  189. #define offsetData                    offsetof(ScriptData, dataTable)
  190. #define offsetStatus                offsetof(ScriptData, statusTable)
  191. #define offsetCommandComplete        offsetof(ScriptData, completeTable)
  192. #define offsetBitBucketIn            offsetof(ScriptData, bitBucketInTable)
  193. #define offsetBitBucketOut            offsetof(ScriptData, bitBucketOutTable)
  194. #define offsetIgnoredMsg            offsetof(ScriptData, ignoredMsgTable)
  195.  
  196. /*
  197.  * The script will be patched when it is first executed. The CTEST0 register will
  198.  * be set to the PB.driverAction value when the script starts to inform the script
  199.  * whether the user's request has a data phase.
  200.  *    CTEST0        Initialized to 0 (no data), 1 (data in), or 2 (data out)
  201.  *                Will be zero on exit if the data phase was executed.
  202.  * A word or two about debugging: as I began debugging, I added a variety of strange
  203.  * code bits, including a number of debugging interrupts to track down a problem
  204.  * (mentioned below). Eventually, I turned on the NCR single-step interrupt and logged
  205.  * every program counter -- this located the bug. The next day, I removed a variety
  206.  * of hacks, replacing them with a better version of the single-step log. I have chosen
  207.  * to leave much of the scaffolding in place to give developers insight as to where
  208.  * one might encounter problems developing to a new, unfamiliar, standard; and how
  209.  * at least one reasonably experienced engineer approaches the problem.
  210.  *
  211.  * The primary problem I encountered was the unavailability of a compiler for the
  212.  * NCR Script "language" -- I have emulated the script by compiling instructions
  213.  * using the somewhat limited C pre-processor, with a simple post-processing "second
  214.  * pass" to resolve labelled statements.
  215.  *
  216.  * All things considered, there were few bugs -- and initial debugging took two
  217.  * very long days. Here are the main points:
  218.  * 1. Longword data had to be byte-swapped for the PCI little-endian environment.
  219.  *    This includes scripts and the Table address and length values.
  220.  * 2. There were a few simple bugs in the script macros -- nothing special, but an
  221.  *    inverted true/false bit took its toll.
  222.  * 3. Single-step recording was added when the script kept wandering through places it
  223.  *    should not have gone. The reason for this is that I was unaware that the Jump
  224.  *    instruction does not Jump unless the "true" bit is set. Apparently, the NCR
  225.  *      chip designer learned to program on a PDP-10.
  226.  * 4. A footnote hidden at the bottom of the page noted that the shift operation
  227.  *      shifts through the carry bit, just in case you want to implement a division
  228.  *      in the script.
  229.  * 5. Added a bunch of extraneous interrupts in order to log precisely why the
  230.  *      script decided to jump to the failure sequence. These can now be removed.
  231.  */
  232. UInt32             gNCRSCSIScript[] = {
  233.     ScriptStart,        /* 0x00                        Script Start (must be at start)    */
  234.     /*
  235.      * This transfer vector must track the definitions in NCR53C825.h
  236.      */
  237.     Jump('BusR'),        /* 0x08    kBusResetScript        Start of Bus Reset Script        */
  238.     Jump('BusX'),        /* 0x10    kBusResetScriptRestart    Bus reset completion        */
  239.     Jump('Scsi'),        /* 0x18    kSCSICommandScript    Start of SCSI Command Script    */
  240.     Jump('Fail'),        /* 0x20    kSCSIRundownScript    Goto I/O rundown                */
  241.     Jump('Loop'),        /* 0x28    kSCSIRestartScript    Goto main SCSI loop                */
  242. /*
  243.  *** SCSI Command Script
  244.  */
  245.  
  246. /*
  247.  * Here we go: select the device and send the Identify message if the device
  248.  * goes to MSG_OUT phase. SCSI-I devices may not do this; we'll clear ATN just in
  249.  * case. Then send the command if the device is in CMD phase, otherwise, fail.
  250.  */
  251. Label('Scsi'),
  252.     SelectATN(offsetDeviceID, 'ReSe'),    /* Interrupt here if selection fails        */
  253.     JumpWhenNotPhase(MSGO, 'Init'),
  254.     MoveFrom(offsetIDMsg, MSGO),
  255. Label('Init'),
  256.     ClearATN(),
  257. /*
  258.  * The main phase-loop -- continue executing commands until we reach Status phase.
  259.  */
  260. Label('Loop'),
  261.     JumpWhenPhase(CMD,                'CmdP'),
  262.     JumpIfPhase(STS,                'Stat'),
  263.     JumpIfPhase(MSGI,                'MsgI'),    /* Message reject?                    */
  264.     JumpIfPhase(DATI,                'Data'),
  265.     JumpIfPhase(DATO,                'Data'),
  266.     DebugInt(kIntFailStrangePhase)
  267.     Jump('Fail'),                                /* Strange phase                    */
  268. /*
  269.  * Data phase common entrance. If CTEST0 is zero, interrupt -- this may be a
  270.  * partial-preparation continuance.
  271.  */
  272. Label('Data'),
  273.     MoveToSFBR(CTEST0),                            /* Is data prepared?                */
  274.     JumpIfEqual(kNCRDriverNoDataPhase, 'NoDa'),    /* Jump if not prepared                */
  275. Label('DaOK'),                                    /* All prepared, so do the transfer    */
  276.     JumpIfPhase(DATI,                'DatI'),    /* Good so far, jump to the proper    */
  277.     JumpIfPhase(DATO,                'DatO'),    /*  phase handler                    */
  278.     DebugInt(kIntDataPhaseExpected)
  279.     Jump('Fail'),                                /* We should be in a data phase        */
  280. Label('NoDa'),
  281.     Int(kIntNeedAnotherPreparation),            /* Is another preparation needed?    */
  282.     MoveToSFBR(CTEST0),                            /* Continued, is it prepared now?    */
  283.     JumpIfNotEqual(0,                'DaOK'),    /* Yes, try the transfer again        */
  284.     DebugInt(kIntPreparationFailed)
  285.     Jump('Fail'),                                /* Run the bit-bucket script        */
  286. /*
  287.  * Command phase - send out the command,
  288.  * TBS: Clear a bit in CTEST0 to signal that we sent out the command.
  289.  */
  290. Label('CmdP'),
  291.     MoveFrom(offsetCommand, CMD),
  292.     Jump('Loop'),
  293. /*
  294.  * Data Out phase: CTEST0 must equal kNCRDriverOutputAllowed
  295.  */
  296. Label('DatO'),
  297.     JumpIfNotEqual(kNCRDriverOutputAllowed,    'DaOF'),
  298.      MoveValueToRegister(kNCRDriverNoDataPhase, CTEST0),
  299.      MoveFrom(offsetData, DATO),
  300.      Jump('Loop'),                        /* Data phase completion                    */
  301. Label('DaOF'),
  302.     DebugInt(kIntDataOutNoData)
  303.     Jump('Fail'),
  304. /*
  305.  * Data In phase: CTEST0 must equal kNCRDriverInputAllowed
  306.  */
  307. Label('DatI'),
  308.     JumpIfNotEqual(kNCRDriverInputAllowed, 'DaIF'),
  309.      MoveValueToRegister(kNCRDriverNoDataPhase, CTEST0),
  310.      MoveFrom(offsetData, DATI),
  311. /*
  312.  * We will get a phase mismatch error here (I think) if we are in a short-
  313.  * transfer situation. In this case, the DBC should be non-zero and the fifo's
  314.  * empty. In this case, the interrupt service routine recovers by retrieving the
  315.  * DBC count (for the actualTransfer computation) and restarting the script.
  316.  */
  317.      Jump('Loop'),                        /* Data phase completion                    */
  318. Label('DaIF'),
  319.     DebugInt(kIntDataInNoData)
  320.     Jump('Fail'),
  321. /*
  322.  * Normal exit through Status Phase - store the status and expect a MSG_IN to follow.
  323.  */
  324. Label('Stat'),
  325.     MoveFrom(offsetStatus, STS),
  326.     JumpWhenPhase(MSGI, 'CoCo'),
  327.     DebugInt(kIntNotMsgInAfterStatus)
  328.      Jump('Fail'),
  329. /*
  330.  * Messge in during normal command processing. About all we can do is look for a
  331.  * Message reject (to the identify message) and ignore it. We should also handle
  332.  * other "reasonable" messages.
  333.  *
  334.  * This needs work -- lots of work -- for a production driver. Also, a production
  335.  * driver needs to be able to send messages (such as "abort") during normal
  336.  * script operation.
  337.  */
  338. Label('MsgI'),
  339.     MoveFrom(offsetIgnoredMsg, MSGI),
  340.     ClearACK(),
  341.     Jump('Loop'),
  342. /*
  343.  * Normal exit at Command Complete message.
  344.  */
  345. Label('CoCo'),
  346.      MoveFrom(offsetCommandComplete, MSGI),
  347. /*
  348.  * Final (successful) exit: ACK the Command Complete Msg and wait for bus-free.
  349.  */
  350. Label('Exit'),
  351.     AllowDisconnect(),
  352.     ClearACK(),
  353.     WaitDisconnect(),
  354.     Int(noErr),
  355. /*
  356.  * Failures during normal processing come here. We bit-bucket the data until the
  357.  * target reaches STATUS phase, then try to handle the STATUS and MSG_IN  messages.
  358.  *
  359.  * This is not quite sufficient: we should check for REQ -- if it is not set,
  360.  * we should set ATN and wait for REQ again. MSGO will then send a SCSI abort
  361.  * byte (we could do this in the script by moving kScsiMsgAbort to the SFBR)
  362.  * Hmm, we could probably do the entire bit bucket in the script by fiddling
  363.  * with the SFBR register.
  364.  */
  365. Label('Fail'),
  366.     JumpIfNotPhase(MSGI, 'Foop'),
  367. Label('FMsI'),
  368.     MoveFrom(offsetBitBucketIn, MSGI),
  369.     ClearACK(),
  370. Label('Foop'),
  371.     JumpWhenPhase(STS,        'FSts'),
  372.     JumpIfPhase(DATI,        'FDaI'),
  373.     JumpIfPhase(DATO,        'FDaO'),
  374.     JumpIfPhase(MSGI,        'FMsI'),
  375.     JumpIfPhase(MSGO,        'FMsO'),
  376.     JumpIfPhase(CMD,        'FCmd'),
  377.     Int(scsiUnableToTerminate),            /* Something is seriously wrong here    */
  378. Label('FCmd'),
  379.     MoveFrom(offsetBitBucketOut, CMD),
  380.     Jump('Foop'),
  381. Label('FDaI'),
  382.     MoveFrom(offsetBitBucketIn, DATI),
  383.     Jump('Foop'),
  384. Label('FDaO'),
  385.     MoveFrom(offsetBitBucketOut, DATO),
  386.     Jump('Foop'),
  387. Label('FMsO'),
  388.     MoveFrom(offsetBitBucketOut, MSGO),
  389.     Jump('Foop'),
  390. /*
  391.  * Status phase after some recoverable error. Get the bytes and quit. Don't try move
  392.  * elaborate recovery here.
  393.  */
  394. Label('FSts'),
  395.     MoveFrom(offsetStatus, STS),
  396.      MoveFrom(offsetCommandComplete, MSGI),
  397.     AllowDisconnect(),
  398.     ClearACK(),
  399.     WaitDisconnect(),
  400.     Int(scsiSequenceFailed),
  401. /*
  402.  * Jump here if this machine was selected or reselected.
  403.  */
  404. Label('ReSe'),
  405.     Int(scsiBusy),                    /* This is probably the wrong error    */
  406.     Jump('ReSe'),
  407.  
  408. /*
  409.  *** Bus Reset Script (This is independent of the SCSI Command script)
  410.  */
  411. Label('BusR'),
  412. /*
  413.  * Assert bus reset - this will interrupt the processor, but the interrupt
  414.  * service routine notes that we asserted reset and will continue at 'BusX'
  415.  */
  416.     MoveValueToRegister(bit3, SCNTL1),                /* This will interrupt            */
  417. Label('NoOp'),                                        /* This might not be needed        */
  418. Label('BusX'),                                        /* Bus reset completion            */
  419.     MoveValueToRegister(0, SCNTL1),
  420.     Int(noErr),
  421. };
  422. ByteCount                    gNCRSCSIScriptSize = sizeof gNCRSCSIScript;
  423.  
  424. #define kMaxLabels            32                        /* Sizeof the label tabel        */
  425. struct LabelTable {
  426.     unsigned long            label;
  427.     unsigned long            scriptIndex;
  428. };
  429. typedef struct LabelTable LabelTable, *LabelTablePtr;
  430.  
  431. int                            NCRLookupSymbol(
  432.         LabelTablePtr            labelTablePtr,
  433.         int                        nLabels,
  434.         unsigned long            symbolName
  435.     );
  436. Boolean                        HasLabel(
  437.         UInt32                    opcode
  438.     );
  439.  
  440.  
  441. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  442.  * Hack to patch the goto addresses in the script. This is a bit more efficient than
  443.  * would really be necessary. It is called when the driver opens..
  444.  */
  445. OSErr
  446. NCRPatchScript(void)
  447. {
  448.         OSErr                    status;
  449.         unsigned long            dcmd;                /* Opcode                        */
  450.         unsigned long            dsps;                /* Second word -- has symbol    */
  451.         int                        scriptIndex;        /* -> Script operation            */
  452.         int                        maxScript;            /* Longwords in script            */
  453.         int                        labelIndex;            /* -> Label in this script op    */
  454.         int                        offset;
  455.         LabelTable                labelTable[kMaxLabels];
  456.         int                        nLabels;
  457.         int                        i;
  458.         
  459.         Trace(NCRPatchScript);
  460.         /*
  461.          * First, check whether we've already patched this script. This will be
  462.          * TRUE if we close and reopen the driver.
  463.          */
  464.         switch (gNCRSCSIScript[1]) {
  465.         case kScriptStartLabel:
  466.             /*
  467.              * This is the first time we've seen this script: patch it and put it
  468.              * into little-endian byte order.
  469.              */
  470.             status = notOpenErr;
  471.             break;
  472.         case 0:
  473.             /*
  474.              * The script has been patched.
  475.              */
  476.             status = noErr;
  477.             break;
  478.         default:
  479.             LogHex(gNCRSCSIScript[1], "\pScript patch indicator wrong");
  480.             status = paramErr;
  481.             break;
  482.         }
  483.         if (status == notOpenErr) {
  484.             /*
  485.              * First call: build the label table.
  486.              */
  487.             nLabels = 0;
  488.             status = noErr;
  489.             maxScript = sizeof gNCRSCSIScript / sizeof (UInt32);
  490.             for (scriptIndex = 0; scriptIndex < maxScript;) {
  491.                 dcmd = gNCRSCSIScript[scriptIndex++];
  492.                 dsps = gNCRSCSIScript[scriptIndex++];
  493.                 if (IsMemoryMoveOp(dcmd))
  494.                     scriptIndex++;
  495.                 if (IsLabel(dcmd)) {
  496.                     if (nLabels >= kMaxLabels)
  497.                         LogString("\pToo many labels");
  498.                     /*
  499.                      * Insertion sort (from Algorithms in C). Since there are only
  500.                      * about 20 labels, we don't need the setup time of a log(n) sort,
  501.                      * such as Shell or Heap sort.
  502.                      */
  503.                     for (i = nLabels; i >= 1 && dsps < labelTable[i - 1].label; --i)
  504.                         labelTable[i] = labelTable[i - 1];
  505.                     labelTable[i].label = dsps;
  506.                     labelTable[i].scriptIndex = scriptIndex;
  507.                     ++nLabels;
  508.                 }
  509.             }
  510.             /*
  511.              * Resolve the labels.
  512.              */
  513.             for (scriptIndex = 0; scriptIndex < maxScript;) {
  514.                 dcmd = gNCRSCSIScript[scriptIndex++];
  515.                 labelIndex = scriptIndex;
  516.                 dsps = gNCRSCSIScript[scriptIndex++];
  517.                 if (IsMemoryMoveOp(dcmd))
  518.                     scriptIndex++;
  519.                 if (HasLabel(dcmd)) {
  520.                     offset = NCRLookupSymbol(labelTable, nLabels, dsps);
  521.                     if (offset > 0) {
  522.                         gNCRSCSIScript[labelIndex] =
  523.                             (offset - scriptIndex) * sizeof gNCRSCSIScript[0];
  524.                     }
  525.                     else {
  526. #if USE_LOG_LIBRARY
  527.                         WriteLogEntry(GLOBAL.logRecordPtr, 'ScrP',
  528.                             LogFormat4(kLogFormatSigned, kLogFormatAddress,
  529.                                         kLogFormatAddress, kLogFormatAddress),
  530.                             labelIndex - 1,
  531.                             dcmd,
  532.                             dsps,
  533.                             "\pUndef script"
  534.                         );
  535. #endif
  536.                         status = paramErr;
  537.                         break;
  538.                     }
  539.                 }
  540.             }
  541.             if (status == noErr) {
  542.                 /*
  543.                  * Byte-reverse each script longword so that it can be accessed by the
  544.                  * NCR chip. This turns the Macintosh big-endian byte-ordering into the
  545.                  * PCI bus little-endian ordering.
  546.                  */
  547.                 for (scriptIndex = 0; scriptIndex < maxScript; scriptIndex++)
  548.                     gNCRSCSIScript[scriptIndex] = EndianSwap32Bit(gNCRSCSIScript[scriptIndex]);
  549.             }
  550.         }
  551.         return (status);
  552. }
  553.  
  554. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  555.  * Resolve a script symbol name, returning the index to the script operation. Return
  556.  * zero if the symbol cannot be found. (No symbol can resolve to zero.) The algorithm
  557.  * was adapted from Kernighan and Ritchie.
  558.  */
  559. int
  560. NCRLookupSymbol(
  561.         LabelTablePtr            labelTablePtr,
  562.         int                        nLabels,
  563.         unsigned long            symbolName
  564.     )
  565. {
  566.         register LabelTablePtr    low;
  567.         register LabelTablePtr    high;
  568.         register LabelTablePtr    mid;
  569.         int                        result;
  570.         
  571.         //** Trace(NCRLookupSymbol);
  572.         low = labelTablePtr;
  573.         high = &labelTablePtr[nLabels];
  574.         result = 0;
  575.         while (low < high) {
  576.             mid = low + (high - low) / 2;
  577.             if (symbolName < mid->label)
  578.                 high = mid;
  579.             else if (symbolName > mid->label)
  580.                 low = mid + 1;
  581.             else /* symbolName == mid->label */ {
  582.                 result = mid->scriptIndex;
  583.                 break;
  584.             }
  585.         }
  586.         return (result);
  587. }
  588.  
  589. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  590.  * HasLabel
  591.  *
  592.  * This tests whether the Script opcode takes a label.
  593.  */
  594. Boolean
  595. HasLabel(
  596.         UInt32                    opcode
  597.     )
  598. {
  599.         Boolean                    result;
  600.         
  601.         //** Trace(HasLabel);
  602.         switch (opcode & (bit31 | bit30)) {
  603.         case (0):                    /* 00 Block move instruction                    */
  604.             result = FALSE;
  605.             break;
  606.         case (bit30):                /* 01 I/O and read/write instructions            */
  607.             switch (opcode & (bit29 | bit28 | bit27)) {
  608.             case 0:                    /* 01 000 Reselect takes a label                */
  609.             case bit28:                /* 01 010 Wait Select takes a label                */
  610.                 result = TRUE;
  611.                 break;
  612.             default:                /* 01 001 Wait Disconnect takes no label        */
  613.                                     /* 01 011 Set takes no label                    */
  614.                                     /* 01 100 Clear takes no label                    */
  615.                 result = FALSE;        /* 01 101..111 are read/write instructions        */
  616.                 break;
  617.             }
  618.             break;
  619.         case (bit31):                /* 10 Transfer Control instructions                */
  620.             switch (opcode & (bit29 | bit28 | bit27)) {
  621.             case 0:                    /* 10 000 Jump    (0x80000000 == NOP)                */
  622.             case bit27:                /* 10 001 Call                                    */
  623.             case bit28:                /* 10 010 Return                                */
  624.                 result = TRUE;
  625.                 break;
  626.             case (bit28 | bit27):    /* 10 011 Interrupt                                */
  627.             default:                /* 10 1xx Reserved                                */
  628.                 result = FALSE;
  629.                 break;
  630.             }
  631.             break;
  632.         case (bit31 | bit30):        /* 11 Memory Move                                */
  633.             result = FALSE;
  634.             break;
  635.         }
  636.         return (result);
  637. }
  638.  
  639.  
  640.